package com.googlecode.jtoy.jee.mail;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.log4j.Logger;

/**
 * 发送邮件
 * 
 * @author shangxj
 * @date Jun 28, 2011 4:15:38 PM
 */
public class MailSender {

	protected static final Logger logger = Logger.getLogger(MailSender.class);

	private String server;// 发送邮件服务器地址
	private int port = 25;
	private String username; // 发送邮件服务器的用户帐号
	private String password; // 发送邮件服务器的用户密码
	private Date sentDate = new Date(); // 发送日期

	private String fromEmail; // 发送者
	private String fromName; // 发送者姓名
	private List<String> toEmails; // 收件人地址
	private List<String> ccEmails; // 抄送地址

	private List<String> bccEmails; // 密送地址
	private String subject; // 邮件主题
	private String content; // 邮件内容

	private static String CONTENT_TYPE = "text/html;charset=UTF-8";

	public MailSender() {

	}

	public boolean sendMail() {

		Session session = Session.getDefaultInstance(getProperties(), null);

		// 开启调试
		if (logger.isDebugEnabled()) {
			session.setDebug(true);
		}

		MimeMessage message = new MimeMessage(session);

		try {
			setAddresses(message);

			// 设置邮件消息的主题
			message.setSubject(this.subject);
			// 设置邮件消息发送的时间
			message.setSentDate(this.sentDate);

			message.setContent(this.contentToMultiPart());

			// 发送邮件
			sendMail0(session, message);
			return true;
		}
		catch (MessagingException e) {
			logger.error("exception occurs when send email(from:" + this.fromEmail + "[" + this.fromName + "]"
					+ ", to: " + this.toEmails.get(0) + "...)," + e.getMessage());
			if (logger.isDebugEnabled()) {
				e.printStackTrace();
			}
		}
		catch (UnsupportedEncodingException e) {
			logger.error("exception occurs when send email(from:" + this.fromEmail + "[" + this.fromName + "]"
					+ ", to: " + this.toEmails.get(0) + "...)," + e.getMessage());
			if (logger.isDebugEnabled()) {
				e.printStackTrace();
			}
		}

		return false;
	}

	/**
	 * 发送邮件
	 * 
	 * @param session
	 * @param message
	 * @throws NoSuchProviderException
	 * @throws MessagingException
	 */
	protected void sendMail0(Session session, MimeMessage message) throws NoSuchProviderException, MessagingException {
		Transport transport = session.getTransport("smtp");
		transport.connect(this.getServer(), this.getUsername(), this.getPassword());
		transport.sendMessage(message, message.getAllRecipients());
		transport.close();
	}

	/**
	 * 处理邮件内容
	 * 
	 * @param message
	 * @throws MessagingException
	 */
	protected Multipart contentToMultiPart() throws MessagingException {
		// 设置邮件消息的主要内容
		Multipart multiPart = new MimeMultipart("related");
		{
			BodyPart bodyPart = new MimeBodyPart();

			Pattern pattern = Pattern.compile("<img\\s+.*?src=\"([^h].*?)\".*?>", Pattern.CASE_INSENSITIVE);

			Matcher matcher = pattern.matcher(this.content);

			String contentWithCid = this.content;
			int i = 0;
			Map<String, String> cidMap = new HashMap<String, String>();
			if (matcher.find()) {
				if (logger.isDebugEnabled()) {
					logger.debug("Match <img /> tag: " + matcher.group());
				}
				String imgId = "img" + i++;
				String imgUrl = matcher.group(1);
				cidMap.put(imgId, imgUrl);
				contentWithCid = contentWithCid.replaceAll(imgUrl, "cid:" + imgId);
			}

			bodyPart.setContent(contentWithCid, CONTENT_TYPE);

			multiPart.addBodyPart(bodyPart);

			Set<Entry<String, String>> entries = cidMap.entrySet();
			for (Entry<String, String> entry : entries) {

				BodyPart imgBodyPart = new MimeBodyPart();
				{
					DataSource source = new FileDataSource(entry.getValue());
					imgBodyPart.setDataHandler(new DataHandler(source));
					String contentId = "<" + entry.getKey() + ">";
					imgBodyPart.setHeader("Content-ID", contentId);
					imgBodyPart.setFileName(entry.getValue());
				}
				multiPart.addBodyPart(imgBodyPart);
			}
		}
		return multiPart;
	}

	/**
	 * 处理邮件地址
	 * 
	 * @param message
	 * @throws MessagingException
	 * @throws UnsupportedEncodingException
	 * @throws AddressException
	 */
	protected void setAddresses(MimeMessage message) throws MessagingException, UnsupportedEncodingException,
			AddressException {
		// 设置邮件消息的发送者
		message.setFrom(new InternetAddress(this.fromEmail, this.fromName));
		// 创建邮件的接收者地址，并设置到邮件消息中
		for (String toEmail : this.toEmails) {
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
		}
		if (this.ccEmails != null && this.ccEmails.size() > 0) {
			for (String ccEmail : this.ccEmails) {
				message.addRecipient(Message.RecipientType.CC, new InternetAddress(ccEmail));
			}
		}
		if (this.bccEmails != null && this.bccEmails.size() > 0) {
			for (String bccEmail : this.bccEmails) {
				message.addRecipient(Message.RecipientType.BCC, new InternetAddress(bccEmail));
			}
		}
	}

	/**
	 * 处理系统属性信息
	 * 
	 * @return
	 */
	protected Properties getProperties() {
		Properties prop = System.getProperties();
		prop.put("mail.smtp.host", this.server);
		prop.put("mail.smtp.port", String.valueOf(this.port));
		prop.put("mail.smtp.auth", "true"); // 设置smtp认证，很关键的一句

		if (this.server.equalsIgnoreCase("smtp.gmail.com") || this.server.endsWith("smtp.googlemail.com")) {
			prop.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
			prop.put("mail.smtp.socketFactory.fallback", "true");
			prop.put("mail.smtp.socketFactory.port", String.valueOf(this.port));
		}
		return prop;
	}

	public void setBccEmails(String... bccEmails) {

		this.bccEmails = Arrays.asList(bccEmails);
	}

	public void setCcEmails(String... ccEmails) {

		this.ccEmails = Arrays.asList(ccEmails);
	}

	public void setToEmails(String... toEamils) {

		this.toEmails = Arrays.asList(toEamils);
	}

	/** (getter for server) */
	public String getServer() {

		return this.server;
	}

	/** (setter for server) */
	public void setServer(String server) {

		this.server = server;
	}

	/** (getter for port) */
	public int getPort() {

		return this.port;
	}

	/** (setter for port) */
	public void setPort(int port) {

		this.port = port;
	}

	/** (getter for username) */
	public String getUsername() {

		return this.username;
	}

	/** (setter for username) */
	public void setUsername(String username) {

		this.username = username;
	}

	/** (getter for password) */
	public String getPassword() {

		return this.password;
	}

	/** (setter for password) */
	public void setPassword(String password) {

		this.password = password;
	}

	/** (getter for sentDate) */
	public Date getSentDate() {

		return this.sentDate;
	}

	/** (setter for sentDate) */
	public void setSentDate(Date sentDate) {

		this.sentDate = sentDate;
	}

	/** (getter for fromEmail) */
	public String getFromEmail() {

		return this.fromEmail;
	}

	/** (setter for fromEmail) */
	public void setFromEmail(String fromEmail) {

		this.fromEmail = fromEmail;
	}

	/** (getter for fromName) */
	public String getFromName() {

		return this.fromName;
	}

	/** (setter for fromName) */
	public void setFromName(String fromName) {

		this.fromName = fromName;
	}

	/** (getter for toEmails) */
	public List<String> getToEmails() {

		return this.toEmails;
	}

	/** (setter for toEmails) */
	public void setToEmails(List<String> toEmails) {

		this.toEmails = toEmails;
	}

	/** (getter for ccEmails) */
	public List<String> getCcEmails() {

		return this.ccEmails;
	}

	/** (setter for ccEmails) */
	public void setCcEmails(List<String> ccEmails) {

		this.ccEmails = ccEmails;
	}

	/** (getter for bccEmails) */
	public List<String> getBccEmails() {

		return this.bccEmails;
	}

	/** (setter for bccEmails) */
	public void setBccEmails(List<String> bccEmails) {

		this.bccEmails = bccEmails;
	}

	/** (getter for subject) */
	public String getSubject() {

		return this.subject;
	}

	/** (setter for subject) */
	public void setSubject(String subject) {

		this.subject = subject;
	}

	/** (getter for content) */
	public String getContent() {

		return this.content;
	}

	/** (setter for content) */
	public void setContent(String content) {

		this.content = content;
	}

	/** (setter for cONTENT_TYPE) */
	public static void setCONTENT_TYPE(String cONTENT_TYPE) {

		CONTENT_TYPE = cONTENT_TYPE;
	}

	/** (getter for cONTENT_TYPE) */
	public static String getCONTENT_TYPE() {

		return CONTENT_TYPE;
	}

}
